<?php
namespace api\service;

use api\service\interfaces\IPaymentService;
use herosphp\bean\Beans;
use herosphp\core\Loader;
use herosphp\core\Log;
use herosphp\lock\SynLockFactory;
use herosphp\utils\AjaxResult;
use payment\models\QueryAccountVo;
use payment\models\TransferVo;
use payment\models\WithdrawVo;
use payment\utils\SerialNumberUtils;
use user\service\interfaces\IUserLogService;

Loader::import('api.service.interfaces.IPaymentService');

/**
 * 支付接口实现
 * @author yangjian<yangjian102621@gmail.com>
 * @since 2016-11-18
 */
class PaymentService implements IPaymentService {

    /**
     * @var \payment\service\PaymentService
     */
    protected $service;

    public function __construct()
    {
        $this->service = Beans::get("payment.payment.service");
    }


    /**
     * 入金
     * @param $userid 大塑用户支付用户ID
     * @param $amount 发生金额
     * @return AjaxResult
     */
    public function inPay($userid, $amount)
    {
        $lock = SynLockFactory::getFileSynLock($userid);
        if ( $lock->tryLock() ) {
            //获取用户信息
            $userService = Beans::get('user.user.service');
            $paywayService = Beans::get('payment.payway.service');
            $user = $userService->getItem($userid);
            $payway = $paywayService->getItem($user['payway']);

            //获取MPE结算户信息
            $appStatModel = Loader::model('appStatAccount');
            $mpe = $appStatModel->getItem(getAPPID());
            if ( $mpe == false ) {
                $lock->unlock(); //解锁
                return new AjaxResult(AjaxResult::OP_FAILURE, LANG('NO_STATEMENT_ACCOUNT'));
            }

            if ( !$this->checkUserPermission($user, $message) ) {
                $lock->unlock(); //解锁
                return new AjaxResult(AjaxResult::OP_FAILURE, $message);
            }

            if ( empty($mpe['virtual_account']) ) { //mpe没有结算户
                $lock->unlock(); //解锁
                return new AjaxResult(AjaxResult::OP_FAILURE, LANG('NO_STATEMENT_ACCOUNT'));
            }

            $reqVo = new TransferVo();
            $reqVo->setBankName($payway['key']);
            $reqVo->setRequestNumber(SerialNumberUtils::generate($payway['serial_prefix']));
            $reqVo->setAmount($amount);
            $reqVo->setPayer($user['virtual_name']);
            $reqVo->setPayerAccount($user['virtual_account']);
            $reqVo->setPayee($mpe['virtual_name']);
            $reqVo->setPayeeAccount($mpe['virtual_account']);
            $reqVo->setRemark("入金操作");
            $res = $this->service->inPay($reqVo);
            if ( $res == false ) {
                $lock->unlock(); //解锁
                return new AjaxResult(AjaxResult::OP_FAILURE, LANG('DEAL_FAILD'));
            } else {

                //1. 增加用户可用余额和累计入金金额
                $userService->beginTransaction();
                $result = $userService->increase(array('balance', 'usable', 'inpay_total'), array($amount,$amount,$amount), $userid);
                if ( $result ) {
                    //2. 添加支付流水
                    $logService = Beans::get('user.userLog.service');
                    $logData = array(
                        'app_id' => getAPPID(),
                        'userid' => $userid,
                        'username' => $user['username'],
                        'type' => IUserLogService::LOG_TYPE_INPAY,
                        'atype' => '+',
                        'serial_no' => $reqVo->getRequestNumber(),
                        //'order_no' => $reqVo->getRequestNumber(),
                        'amount' => $amount,
                        'balance' => $user['balance']+$amount,
                        'usable' => $user['usable']+$amount,
                        'out_frozen' => $user['out_frozen'],
                        'addtime' => date('Y-m-d H:i:s')
                    );

                    if ( $logService->add($logData) ) {
                        $userService->commit();
                    } else {
                        $userService->rollback();
                        Log::info("入金时增加账户余额操作发生异常. mpeid : ".getAPPID().", userid : {$userid}, amount : {$amount}");
                    }
                }

                $lock->unlock(); //解锁
                return new AjaxResult(AjaxResult::OP_SUCCESS, LANG('DEAL_SUCCESS'));
            }

        }
        return new AjaxResult(AjaxResult::OP_FAILURE, "系统繁忙，请稍后再试.");
    }

    /**
     * 入金
     * @param $userid 大塑用户支付用户ID
     * @param $amount 发生金额
     * @return AjaxResult
     */
    public function outPay($userid, $amount)
    {
        $lock = SynLockFactory::getFileSynLock($userid);
        if ( $lock->tryLock() ) { //锁定进程
            //获取用户信息
            $userService = Beans::get('user.user.service');
            $paywayService = Beans::get('payment.payway.service');
            $user = $userService->getItem($userid);
            $payway = $paywayService->getItem($user['payway']);

            //判断用户的可用余额是否充足
            if ($user['usable'] < $amount || $user['usable'] <= 0) {
                $lock->unlock(); //解锁
                return AjaxResult::fail(LANG('NO_AVA_USABLE'));
            }

            if (!$this->checkUserPermission($user, $message)) {
                $lock->unlock(); //解锁
                return new AjaxResult(AjaxResult::OP_FAILURE, $message);
            }

            //获取MPE结算户信息
            $appStatModel = Loader::model('appStatAccount');
            $mpe = $appStatModel->getItem(getAPPID());
            if ($mpe == false) {
                $lock->unlock(); //解锁
                return new AjaxResult(AjaxResult::OP_FAILURE, LANG('NO_STATEMENT_ACCOUNT'));
            }

            //1. 减少用户可用余额, 增加用户出累计出金
            $userService->beginTransaction();
            $result = $userService->reduce(array('balance', 'usable', 'outpay_total'), array($amount, $amount, -$amount), $userid);
            if ($result) {
                //2. 添加支付流水
                $serilNo = SerialNumberUtils::generate($payway['serial_prefix']);
                $logService = Beans::get('user.userLog.service');
                $logData = array(
                    'app_id' => getAPPID(),
                    'userid' => $userid,
                    'username' => $user['username'],
                    'type' => IUserLogService::LOG_TYPE_OUTPAY,
                    'atype' => '-',
                    'serial_no' => $serilNo,
                    //'order_no' => $reqVo->getRequestNumber(),
                    'amount' => $amount,
                    'balance' => $user['balance'] - $amount,
                    'usable' => $user['usable'] - $amount,
                    'out_frozen' => $user['out_frozen'],
                    'addtime' => date('Y-m-d H:i:s')
                );

                if ($logService->add($logData)) {
                    $userService->commit();
                } else {
                    $userService->rollback();
                    $lock->unlock(); //解锁
                    Log::info("出金时减少账户余额操作发生异常. mpeid : " . getAPPID() . ", userid : {$userid}, amount : {$amount}");
                    return new AjaxResult(AjaxResult::OP_FAILURE, LANG('DEAL_FAILD'));
                }

                //向银行发送出金请求
                $reqVo = new TransferVo();
                $reqVo->setBankName($payway['key']);
                $reqVo->setRequestNumber($serilNo);
                $reqVo->setAmount($amount);
                $reqVo->setPayer($mpe['virtual_name']);
                $reqVo->setPayerAccount($mpe['virtual_account']);
                $reqVo->setPayee($user['virtual_name']);
                $reqVo->setPayeeAccount($user['virtual_account']);
                $reqVo->setRemark("广发内部转账接口");
                $res = $this->service->outPay($reqVo);
                if ($res == false) {
                    Log::info("发送银行出金请求失败. mpeid : " . getAPPID() . ", userid : {$userid}, amount : {$amount}");
                }
            }
            $lock->unlock(); //解锁
            return new AjaxResult(AjaxResult::OP_SUCCESS, LANG('DEAL_SUCCESS'));

        }//end of lock
        return new AjaxResult(AjaxResult::OP_FAILURE, "系统繁忙，请稍后再试.");
    }

    /**
     * 查询余额
     * @param $userid 大塑用户支付用户ID
     * @return AjaxResult
     */
    public function getBalance($userid)
    {
        $userService = Beans::get('user.user.service');
        $paywayService = Beans::get('payment.payway.service');
        $user = $userService->getItem($userid);
        $payway = $paywayService->getItem($user['payway']);

        if ( !$this->checkUserPermission($user, $message) ) {
            return new AjaxResult(AjaxResult::OP_FAILURE, $message);
        }

        $reqVo = new QueryAccountVo();
        $reqVo->setBankName($payway['key']);
        $reqVo->setRequestNumber(SerialNumberUtils::generate($payway['serial_prefix']));
        $reqVo->setAccount($user['virtual_account']);
        $usable = $this->service->getBalance($reqVo);
        if ( $usable === false ) {
            return new AjaxResult(AjaxResult::OP_FAILURE, LANG('DEAL_FAILD'));
        } else {
            return new AjaxResult(AjaxResult::OP_SUCCESS, LANG('DEAL_SUCCESS'), array('balance' => $usable));
        }

    }

    /**
     * 提现
     * @param $userid 大塑用户支付用户ID
     * @param $amount 发生金额
     * @return AjaxResult
     */
    public function withdraw($userid, $amount)
    {
        $lock = SynLockFactory::getFileSynLock($userid);
        if ( $lock->tryLock() ) { //锁定进程
            $userService = Beans::get('user.user.service');
            $paywayService = Beans::get('payment.payway.service');
            $bankService = Beans::get('payment.bank.service');
            $user = $userService->getItem($userid);
            $payway = $paywayService->getItem($user['payway']);
            $bank = $bankService->getItem($user['sign_bank_id']);

            if (!$this->checkUserPermission($user, $message)) {
                $lock->unlock(); //解锁
                return new AjaxResult(AjaxResult::OP_FAILURE, $message);
            }

            $reqVo = new WithdrawVo();
            $reqVo->setBankName($payway['key']);
            $reqVo->setRequestNumber(SerialNumberUtils::generate($payway['serial_prefix']));
            $reqVo->setPayer($user['virtual_name']);
            $reqVo->setPayerAccount($user['virtual_account']);
            $reqVo->setPayee($user['virtual_name']); //定向出金，只能出金到和附属账户同名的其他帐号
            $reqVo->setPayeeAccount($user['card_no']);
            $reqVo->setPayeeBank($user['sign_bank_branch']); //收款银行
            $reqVo->setPayeeBankAdd($user['sign_bank_branch']);
            $reqVo->setPaymentBankid($user['sign_bank_code']); //联行号
            $reqVo->setPayeeBankCode($bank['bankcode']); //银行代码，用来区分是同行还是跨行，跨行转账需要收取2元/笔手续费
            $reqVo->setRemark("广发提现接口");
            $reqVo->setAmount($amount);

            $res = $this->service->withdraw($reqVo);
            if ($res === false) {
                $lock->unlock(); //解锁
                return new AjaxResult(AjaxResult::OP_FAILURE, LANG('DEAL_FAILD'));
            } else {

                //1. 增加用户累计提现金额
                $userService->beginTransaction();
                $result = $userService->increase('outcash_total', $amount, $userid);
                if ( $result ) {
                    //2. 添加支付流水
                    $logService = Beans::get('user.userLog.service');
                    $logData = array(
                        'app_id' => getAPPID(),
                        'userid' => $userid,
                        'username' => $user['username'],
                        'type' => IUserLogService::LOG_TYPE_CASH,
                        'serial_no' => $reqVo->getRequestNumber(),
                        //'order_no' => $reqVo->getRequestNumber(),
                        'amount' => $amount,
                        'balance' => $user['balance'],
                        'usable' => $user['usable'],
                        'out_frozen' => $user['out_frozen'],
                        'addtime' => date('Y-m-d H:i:s')
                    );
                    if ( $logService->add($logData) ) {
                        $userService->commit(); //事务提交
                    } else {
                        $userService->rollback();
                        $lock->unlock(); //解锁
                        Log::info("记录提现流水失败，操作回滚. mpeid : " . getAPPID() . ", userid : {$userid}, amount : {$amount}");
                        return new AjaxResult(AjaxResult::OP_FAILURE, LANG('DEAL_FAILD'));
                    }
                }


                $lock->unlock();//解锁
                return new AjaxResult(AjaxResult::OP_SUCCESS, LANG('DEAL_SUCCESS'));
            }
        } //end of lock

        return new AjaxResult(AjaxResult::OP_FAILURE, "系统繁忙，请稍后再试.");
    }

    /**
     * 检测用户操作权限
     * @param $user 用户
     * @param $message 错误信息
     * @return bool
     */
    protected function checkUserPermission($user, &$message) {
        if ( $user['ischecked'] != 2 ) {
            $message = LANG('DEAL_FAILD').', '.LANG('USER_NOT_CHECKED');
            return false;
        }
        if ( $user['sign_status'] != 1 ) {
            $message = LANG('DEAL_FAILD').', '.LANG('USER_NOT_SIGNED');
            return false;
        }
        return true;
    }
}
